import socket
import threading
import base64
from urllib.parse import urlparse

import requests
import select
import socks
import winreg


def handle_http_proxy(client_socket, proxy_socket, proxy_user, proxy_pass, first_data):
    # 生成HTTP代理认证头
    auth = base64.b64encode(f"{proxy_user}:{proxy_pass}".encode()).decode()
    proxy_auth_header = f"Proxy-Authorization: Basic {auth}\r\n"
    
    if first_data.startswith(b"CONNECT"):
        # 处理HTTPS请求
        modified_data = first_data.decode().replace("\r\n", f"\r\n{proxy_auth_header}", 1)
        proxy_socket.sendall(modified_data.encode())
        
        try:
            proxy_socket.settimeout(10)
            response = proxy_socket.recv(4096)
            if not response:
                print("代理服务器没有响应")
                return False
                
            response_text = response.decode('utf-8', errors='ignore')
            print(f"代理服务器响应: {response_text[:200]}")
            
            if b"407 Proxy Authentication Required" in response:
                print("代理认证失败")
                return False
            elif b"403 Forbidden" in response:
                print("代理服务器拒绝访问")
                return False
                
            client_socket.send(response)
            return b"200 Connection established" in response
            
        except socket.timeout:
            print("等待代理响应超时")
            return False
        except Exception as e:
            print(f"处理CONNECT请求错误: {e}")
            return False
    else:
        # 处理HTTP请求
        modified_data = first_data.decode().replace("\r\n", f"\r\n{proxy_auth_header}", 1)
        proxy_socket.sendall(modified_data.encode())
        return True


def forward_data(client_socket, proxy_socket, proxy_auth_header="", is_http=False):
    while True:
        try:
            r, w, e = select.select([client_socket, proxy_socket], [], [], 60)
            if not r:  # 超时检查
                print("数据转发超时，关闭连接")
                break

            if client_socket in r:
                try:
                    data = client_socket.recv(4096)
                    if not data:
                        # print("客户端关闭连接")
                        break
                    proxy_socket.sendall(data)
                except (ConnectionError, socket) as e:
                    print(f"客户端连接异常: {e}")
                    break

            if proxy_socket in r:
                try:
                    data = proxy_socket.recv(4096)
                    if not data:
                        print("代理服务器关闭连接")
                        break
                    client_socket.send(data)
                except (ConnectionError, socket) as e:
                    print(f"代理服务器连接异常: {e}")
                    break

        except Exception as e:
            print(f"数据转发错误: {str(e)}")
            break

def get_windows_proxy():
    try:
        reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 
            r'Software\Microsoft\Windows\CurrentVersion\Internet Settings', 
            0, winreg.KEY_ALL_ACCESS)
        proxy_server = winreg.QueryValueEx(reg_key, 'ProxyServer')[0]
        proxy_enable = winreg.QueryValueEx(reg_key, 'ProxyEnable')[0]
        if proxy_enable and proxy_server:
            proxy_host, proxy_port = proxy_server.split(':')
            return proxy_host, int(proxy_port)
        return None, None
    except Exception as e:
        # printing(f"获取系统代理设置失败: {e}")
        return None, None

def create_proxy_socket(target_proxy, proxy_user, proxy_pass):
    proxy_url = urlparse(target_proxy)
    target_host = proxy_url.hostname
    target_port = proxy_url.port
    
    proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    proxy_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    
   
    # 代理检查系统代理
    system_proxy_host, system_proxy_port = get_windows_proxy()
    print(f"系统代理设置: {system_proxy_host}:{system_proxy_port}")
    if system_proxy_host and system_proxy_port:
        proxy_socket = socks.socksocket()
        proxy_socket.set_proxy(
            proxy_type=socks.HTTP,
            addr=system_proxy_host,
            port=system_proxy_port
        )
    
    return proxy_socket, target_host, target_port, proxy_url.scheme

def handle_socks5_connection(proxy_socket, target_host, target_port, proxy_user, proxy_pass):
    try:
        # 1. 发送版本和所有支持的认证方法
        proxy_socket.sendall(b'\x05\x03\x00\x01\x02')  # 支持无认证(0x00)、GSSAPI(0x01)和用户名密码(0x02)
        
        # 2. 接收服务器选择的认证方法
        auth_method = proxy_socket.recv(2)
        # print(f"服务器返回认证方法: {auth_method.hex()}")
        
        if auth_method[1] == 0x00:  # 无需认证
            print("服务器选择无认证方式")
            return True
        elif auth_method[1] == 0x02:  # 用户名密码认证
            # 尝试两种不同的认证格式
                      
            auth_methods = [
                # 方法1：标准格式
                lambda: bytearray([0x01, len(proxy_user)]) + proxy_user.encode() + 
                       bytearray([len(proxy_pass)]) + proxy_pass.encode(),
                # 方法2：替代格式
                lambda: b'\x01' + bytes([len(proxy_user)]) + proxy_user.encode('ascii') + 
                       bytes([len(proxy_pass)]) + proxy_pass.encode('ascii'),
                # 方法3：UTF-8格式
                lambda: b'\x01' + bytes([len(proxy_user)]) + proxy_user.encode() + 
                       bytes([len(proxy_pass)]) + proxy_pass.encode()
            ]
            
            for auth_method_func in auth_methods:
                auth_data = auth_method_func()
                # print(f"尝试认证数据: {auth_data.hex()}")
                proxy_socket.sendall(auth_data)
                
                auth_response = proxy_socket.recv(2)
                print(f"认证响应: {auth_response.hex()}")
                
                if auth_response[1] == 0x00:
                    print("认证成功")
                    return True
                    
                # 如果失败，尝试重新连接并使用下一种认证方式
                if len(auth_methods) > 1:
                    proxy_socket.close()
                    proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    proxy_socket.connect((target_host, target_port))
                    proxy_socket.sendall(b'\x05\x03\x00\x01\x02')
                    auth_method = proxy_socket.recv(2)
            
            print("所有认证方式都失败")
            return False
        else:
            print(f"不支持的认证方法: {auth_method[1]}")
            return False
            
        return True
    except Exception as e:
        print(f"SOCKS5认证错误: {e}")
        return False


def handle_socks5_request(proxy_socket, host, port):
    try:
        # 构建SOCKS5连接请求
        connect_req = b'\x05\x01\x00\x03' + bytes([len(host)]) + \
                     host.encode() + port.to_bytes(2, 'big')
        proxy_socket.sendall(connect_req)
        
        # 接收连接响应
        response = proxy_socket.recv(4)
        if response[1] != 0x00:
            print("SOCKS5连接请求失败")
            return False
            
        # 跳过响应中的地址部分
        if response[3] == 0x01:  # IPv4
            proxy_socket.recv(6)
        elif response[3] == 0x03:  # 域名
            domain_len = proxy_socket.recv(1)[0]
            proxy_socket.recv(domain_len + 2)
        elif response[3] == 0x04:  # IPv6
            proxy_socket.recv(18)
            
        return True
    except Exception as e:
        print(f"SOCKS5请求错误: {e}")
        return False

def handle_client(client_socket, target_proxy, proxy_user, proxy_pass):
    try:
        proxy_socket, target_host, target_port, scheme = create_proxy_socket(
            target_proxy, proxy_user, proxy_pass
        )
        
        try:
            proxy_socket.connect((target_host, target_port))
            print(f"成功连接到目标代理: {target_host}:{target_port}")
            
            if scheme == 'socks5':
                # 尝试 SOCKS5 认证
                if not handle_socks5_connection(proxy_socket, target_host, target_port, proxy_user, proxy_pass):
                    print("SOCKS5 认证失败，切换到 HTTP 模式")
                    # 关闭旧连接
                    proxy_socket.close()
                    
                    # 创建新连接并使用 HTTP 模式
                    proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    proxy_socket.connect((target_host, target_port))
                    scheme = 'http'  # 切换到 HTTP 模式
        except Exception as e:
            print(f"连接目标代理失败: {e}")
            return
            
            
        first_data = client_socket.recv(4096)
        if not first_data:
            return
            
        # print(f"收到客户端请求: {first_data[:200]}")
        
        if scheme == 'socks5':
            if first_data.startswith(b"CONNECT"):
                # 处理HTTPS请求
                host_port = first_data.split()[1].decode()
                host, port = host_port.split(':')
                port = int(port)
                
                if handle_socks5_request(proxy_socket, host, port):
                    client_socket.send(b"HTTP/1.1 200 Connection established\r\n\r\n")
                    # print(f"已建立到 {host}:{port} 的HTTPS隧道")
                else:
                    return
            else:
                # 处理HTTP请求
                host = None
                for line in first_data.split(b'\r\n'):
                    if line.startswith(b'Host: '):
                        host = line[6:].decode()
                        break
                
                if host:
                    if ':' in host:
                        host, port = host.split(':')
                        port = int(port)
                    else:
                        port = 80
                    
                    if handle_socks5_request(proxy_socket, host, port):
                        proxy_socket.sendall(first_data)
                        print(f"已转发HTTP请求到 {host}:{port}")
                    else:
                        return
        else:
            # HTTP代理处理逻辑保持不变
            if handle_http_proxy(client_socket, proxy_socket, proxy_user, proxy_pass, first_data):
                forward_data(client_socket, proxy_socket, 
                           proxy_auth_header=f"Proxy-Authorization: Basic {base64.b64encode(f'{proxy_user}:{proxy_pass}'.encode()).decode()}\r\n",
                           is_http=True)
                return
                
        # 开始数据转发
        forward_data(client_socket, proxy_socket)
        
    except Exception as e:
        print(f"处理客户端连接错误: {e}")
    finally:
        client_socket.close()
        proxy_socket.close()

def start_local_proxy(local_port, target_proxy, proxy_user, proxy_pass):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 确保可以重用端口
    server.bind(('0.0.0.0', local_port))
    server.listen(5)
    print(f"本地代理已启动: 0.0.0.0:{local_port} -> {target_proxy}")
     # 添加停止标志
    current_thread = threading.current_thread()
    current_thread.stop_flag = False
    while not getattr(current_thread, "stop_flag", False):
        try:
            server.settimeout(1)  # 设置超时，以便检查停止标志
            try:
                client_sock, addr = server.accept()
                print(f"接受来自 {addr} 的连接")
                handler = threading.Thread(
                    target=handle_client,
                    args=(client_sock, target_proxy, proxy_user, proxy_pass)
                )
                handler.start()
            except socket.timeout:
                continue
        except Exception as e:
            print(f"接受客户端连接错误: {e}")
    
    # 关闭服务器socket
    try:
        server.close()
        print(f"本地代理已停止: {local_port}")
    except Exception as e:
        print(f"关闭代理服务器错误: {e}")
if __name__ == "__main__":
    proxies = [
        # 本地端口, 目标代理地址, 用户名, 密码
        # (8888, "socks5://149.52.51.35:42001", "v9jgtn", "xb5sr25k"),
        # (8889, "socks5://global.711proxy.com:20000", "USER820162-zone-custom-session-35612364", "ccc777"),
        # (8890, "socks5://103.115.171.231:50100", "seajourjaden", "84T3eM3XVg"),
        (8889, "socks5://129.146.176.23:45029", "hBd68d0e56cdc687", "fa7lB0LohWNIFngV3d")
        # (8888, "http://117.68.10.44:55004", "t8niqp", "w2cmumi8")
        # (8888, "http://156.243.252.56:42001", "y3utar", "iytpz8gf")
        # (8888, "http://103.52.2.221:42001", "sj37jy", "pjtdunrj")
    ]

    for port, target, user, pwd in proxies:
        threading.Thread(
            target=start_local_proxy,
            args=(port, target, user, pwd)
        ).start()
        print(f"启动代理服务: {port} -> {target}")